package crypto

import (
	"encoding/hex"
	"math/big"

	"hundsun.com/hsl/hschain/common/crypto"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/native"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/rtda"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/rtda/heap"
)

func init() {
	_crypto(sha256str, "sha256", "(Ljava/lang/String;)Ljava/lang/String;")
	_crypto(sha256hash, "sha256", "([B)Ljava/lang/String;")
	_crypto(homomorphicAdd, "homomorphicAdd", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B")
	_crypto(homomorphicAddCipher, "homomorphicAddCipher", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B")
	_crypto(homomorphicMul, "homomorphicMul", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B")
}
func _crypto(method native.Method, name, desc string) {
	native.Register("com/hundsun/hsl2/basecontract/crypto/ContractCrypto", name, desc, method)
}

func sha256str(frame *rtda.Frame) {
	source := frame.GetLocalVar(1).Ref.JSToGoStr()
	hashBytes, err := crypto.HashWithOpts([]byte(source), "SHA256")
	var hashStr string
	if err != nil {
		hashStr = ""
	} else {
		hashStr = hex.EncodeToString(hashBytes)
	}
	str := frame.Thread.Runtime.JSFromGoStrNoIntern(hashStr)
	frame.PushRef(str)
}

func sha256hash(frame *rtda.Frame) {
	preimgBytes := frame.GetLocalVar(1).Ref.GetGoBytes()
	hashBytes, err := crypto.HashWithOpts(preimgBytes, "SHA256")
	var hashStr string
	if err != nil {
		hashStr = ""
	} else {
		hashStr = hex.EncodeToString(hashBytes)
	}
	str := frame.Thread.Runtime.JSFromGoStrNoIntern(hashStr)
	frame.PushRef(str)
}

func homomorphicAddCipher(frame *rtda.Frame) {
	nstr := frame.GetLocalVar(1).Ref.JSToGoStr()
	op1str := frame.GetLocalVar(2).Ref.JSToGoStr()
	op2str := frame.GetLocalVar(3).Ref.JSToGoStr()
	pubkey := GetPulicKey(nstr)
	op1, okOp1 := new(big.Int).SetString(op1str, 0)
	op2, okOp2 := new(big.Int).SetString(op2str, 0)
	class := frame.Thread.Runtime.BootLoader().LoadClass("[B")
	if pubkey != nil && okOp1 && okOp2 {
		sum := AddCipher(pubkey, op1.Bytes(), op2.Bytes())
		arrRef := heap.NewArray(class, uint(len(sum)))
		for i := 0; i < int(len(sum)); i++ {
			arrRef.GetGoBytes()[i] = byte(sum[i])
		}
		frame.PushRef(arrRef)
	} else {
		arrRef := heap.NewArray(class, 0)
		frame.PushRef(arrRef)
	}
}
func homomorphicAdd(frame *rtda.Frame) {
	nstr := frame.GetLocalVar(1).Ref.JSToGoStr()
	op1str := frame.GetLocalVar(2).Ref.JSToGoStr()
	op2str := frame.GetLocalVar(3).Ref.JSToGoStr()
	pubkey := GetPulicKey(nstr)
	op1, okOp1 := new(big.Int).SetString(op1str, 0)
	op2, okOp2 := new(big.Int).SetString(op2str, 0)
	class := frame.Thread.Runtime.BootLoader().LoadClass("[B")
	if pubkey != nil && okOp1 && okOp2 {
		sum := Add(pubkey, op1.Bytes(), op2.Bytes())
		arrRef := heap.NewArray(class, uint(len(sum)))
		for i := 0; i < int(len(sum)); i++ {
			arrRef.GetGoBytes()[i] = byte(sum[i])
		}
		frame.PushRef(arrRef)
	} else {
		arrRef := heap.NewArray(class, 0)
		frame.PushRef(arrRef)
	}
}
func homomorphicMul(frame *rtda.Frame) {
	nstr := frame.GetLocalVar(1).Ref.JSToGoStr()
	op1str := frame.GetLocalVar(2).Ref.JSToGoStr()
	op2str := frame.GetLocalVar(3).Ref.JSToGoStr()
	pubkey := GetPulicKey(nstr)
	op1, okOp1 := new(big.Int).SetString(op1str, 0)
	op2, okOp2 := new(big.Int).SetString(op2str, 0)
	class := frame.Thread.Runtime.BootLoader().LoadClass("[B")
	if pubkey != nil && okOp1 && okOp2 {
		sum := Mul(pubkey, op1.Bytes(), op2.Bytes())
		arrRef := heap.NewArray(class, uint(len(sum)))
		for i := 0; i < int(len(sum)); i++ {
			arrRef.GetGoBytes()[i] = byte(sum[i])
		}
		frame.PushRef(arrRef)
	} else {
		arrRef := heap.NewArray(class, 0)
		frame.PushRef(arrRef)
	}
}
